#This file is part of Cute-Client.
#
#Cute-Client is free software: you can redistribute it and/or modify
#it under the terms of the GNU Lesser General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#Cute-Client is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU Lesser General Public License for more details.
#
#You should have received a copy of the GNU Lesser General Public License
#along with Cute-Client.  If not, see <http://www.gnu.org/licenses/>.

import json
import socket

class CSSession(object):
    '''
    CSSession object is the reprentation of a connecion with a session
    on the cute-server side.
    @author Aurelien Deharbe
    @version 1.0
    '''
    
    def __init__(self):
        '''
        Construct and connect a session object on the first free port
        from 5001 to 8000
        @raise CSException If there is a connexion problem,
        or if there is no available port from 5001 to 8000
        '''
        self._callbacks = {}
        self._port = 5001;
        self._server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1);
        cont = True;
        while cont:
            try:
                self._server.bind(('', self._port))
                cont = False
            except socket.error:
                self._port = self._port + 1
        self._server.listen(1)
    
    def qs(self, cmd):
        '''
        Send a command to the cute-server session and return its result
        @param cmd The command to be executed on the session
        @return The command response
        @raise CSException If there is a connexion problem
        '''
        cmd + "\n"
        self._socket.send(cmd)
        ret = self._socket.recv(1024)
        result = json.loads(ret)
        return result[-1]
    
    def registerCallback(self, obj, evt, clb):
        '''
        Register a callback on a distant event
        @param obj Name of the event handler
        @param evt Name of the QT event
        @param clb Callback function
        @raise CSException If there is a connexion problem
        '''
        self.registerTypedCallback(obj, evt, clb, "")
    
    def registerDataCallback(self, data, clb):
        '''
        Register a callback on distant data changing
        @param data Name of the data
        @param clb Callback function
        @raise CSException If there is a connexion problem
        '''
        self.registerTypedCallback(data, "qsdatachanged", clb, "")
    
    def registerTypedCallback(self, obj, evt, clb, tps):
        '''
        Register a callback on distant typed event
        @param obj Name of the event handler
        @param evt Name of the QT event
        @param clb Callback function
        @param tps Arguments types list (separated by ',')
        @raise CSException If there is a connexion problem
        '''
        cmd = "scripts.subscribe('" + obj
        cmd = cmd + "','" + evt + "','" + tps + "','#me#')\n"
        self._socket.send(cmd)
        ret = self._socket.recv(1024).strip()
        ret = json.loads(ret)
        
        for i in ret:
            self._callbacks[i + "." + evt] = clb
    
    def mainloop(self):
        '''
        Enter in the cute-client mainloop
        @raise CSException If there is a connexion problem
        '''
        while True:
            ret = self._socket.recv(1024).strip()
            if not ret:
                break
            jarray = json.loads(ret)
            obj = jarray[0]
            evt = obj + "." + jarray[1]
            if not evt in self._callbacks.keys():
                continue
            args = jarray[2:]
            if args:
                self._callbacks[evt](obj, args)
            else:
                self._callbacks[evt](obj)
    
    def putData(self, name, value):
        '''
        Put a data on the cute-server session
        @param name Name of the data
        @param value Value of the data
        @raise CSException If there is a connexion problem
        '''
        value = json.dumps(value)
        cmd = "json.loads('" + value + "')"
        cmd = cmd + " -> " + name[1:-1] + "\n"
        self._socket.send(cmd)
        self._socket.recv(1024)
    
    def getData(self, name):
        '''
        Retrieve the value of a distant data
        @param name Name of the data
        @return Data retrieved
        @raise CSException If there is a connexion problem
        '''
        cmd = name + "\n"
        self._socket.send(cmd)
        ret = self._socket.recv(1024)
        result = json.loads(ret)
        return result[-1]
    
    def getPort(self):
        '''
        Get the local port of socket connected with the cute-server session
        @return The local port
        '''
        return self._port
    
    def accept(self):
        '''
        Wait for the entering connexion from the cute-server session
        '''
        self._socket = self._server.accept()[0]
        self._server.close()
